/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.tor.tribes.reportserver.util;

import de.tor.tribes.reportserver.MyVaadinApplication;
import de.tor.tribes.reportserver.PersistenceImpl;
import de.tor.tribes.types.DSBuilding;
import de.tor.tribes.types.DSUnit;
import de.tor.tribes.types.ParserConfiguration;
import de.tor.tribes.types.ParserPattern;
import de.tor.tribes.types.ZebraReport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Hashtable;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;

/**
 *
 * @author Torridity
 */
public class ZebraReportParser {

    private String serverId = null;
    private List<DSBuilding> buildings;
    private List<DSUnit> units;
    private ParserConfiguration pConfig;
    private Hashtable<String, DSUnit> unitsByPlainName;
    private PersistenceImpl persistence;

    public ZebraReportParser(PersistenceImpl pPersistence, String pServer) {
        persistence = pPersistence;
        serverId = pServer;
        buildings = pPersistence.getBuildings();
        pConfig = (ParserConfiguration)pPersistence.merge(pPersistence.getParserConfiguration(serverId));
        units = pPersistence.getUnits();
        unitsByPlainName = new Hashtable<String, DSUnit>();
        for (DSUnit unit : units) {
            unitsByPlainName.put(unit.getPlainName(), unit);
        }
    }

    public ZebraReport parse(String pData) throws ParserException {
        int startIdx = 0;
        try {
            ZebraReport report = new ZebraReport();
            report.setServerId(serverId);
            boolean totalLoss = parseTotalLoss(pData, report);

            startIdx = parseSendTime(pData, report, startIdx);

            if (!totalLoss) {
                startIdx = parseWinner(pData, report, startIdx);
            }
            startIdx = parseLuck(pData, report, startIdx);

            startIdx = parseMoral(pData, report, startIdx);

            startIdx = parseAttacker(pData, report, startIdx);

            parseDefender(pData, report, startIdx);

            parseTroops(pData, report, startIdx);

            if (!totalLoss) {
                parseBuildings(pData, report, startIdx);
                startIdx = parseSpiedResources(pData, report, startIdx);
                startIdx = parseTroopsOnTheWay(pData, report, startIdx);
                parseTroopsOutside(pData, report, startIdx);

                startIdx = parseHaul(pData, report, startIdx);

            }
            startIdx = parseWallDamage(pData, report, startIdx);

            startIdx = parseCatapultDamage(pData, report, startIdx);

            if (!totalLoss) {
                parseAcceptance(pData, report, startIdx);
            }
            return report;
        } catch (ParserException pe) {
            //re-throw this
            throw pe;
        } catch (Throwable t) {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.SEVERE, "Failed to parse report due to an unknown error: " + pData, t);
            throw new ParserException("Unknown error while parsing.");
        }
    }

    private boolean parseTotalLoss(String pData, ZebraReport pReport) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.TOTAL_LOSS));
        Matcher matcher = p.matcher(pData);
        if (matcher.find()) {
            pReport.setWon(false);
            pReport.isNot(ZebraReport.REPORT_COLOR.BLUE, ZebraReport.REPORT_COLOR.GREEN, ZebraReport.REPORT_COLOR.YELLOW, ZebraReport.REPORT_COLOR.RED);
            return true;
        }
        return false;
    }

    private int parseWinner(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.WINNER));
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            pReport.setWon(matcher.group(1).equals(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.ATTACKER)));
            return matcher.end();
        } else {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine winner in report: {0}", pData);
            throw new ParserException("Could not determine winner");
        }
    }

    private int parseSendTime(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.SEND) + "\\s+(.*)");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            try {
                pReport.setSent(new SimpleDateFormat(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.SEND_DATE_FORMAT)).parse(matcher.group(1)).getTime());
                return matcher.end();
            } catch (ParseException pe) {
                Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to parse send time in report: {0}", pData);
                throw new ParserException("Could not parse send time", pe);
            }
        } else {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine send time in report: {0}", pData);
            throw new ParserException("Could not determine send time");
        }
    }

    private int parseLuck(String pData, ZebraReport pReport, int pStart) throws ParserException {
        //Pattern p = Pattern.compile(luckPattern + "[[^\\-0-9]*[^0-9]]+([\\-0-9]*[0-9]+\\.[0-9]+)\\%");
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.LUCK) + "[[^\\-0-9]]*([\\-]?[0-9]*[0-9]+\\.[0-9]+)\\%");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            //matcher.group(1).equals("Angreifers");
            pReport.setLuck(Double.parseDouble(matcher.group(2)));
            return matcher.end();
        } else {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine luck in report: {0}", pData);
            throw new ParserException("Could not determine luck");
        }
    }

    private int parseMoral(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.MORAL) + ":\\s+([0-9]+)\\%");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            pReport.setMoral(Short.parseShort(matcher.group(1)));
            return matcher.end();
        } else {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine moral in report: {0}", pData);
            throw new ParserException("Could not determine moral");
        }
    }

    private int parseAttacker(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.ATTACKER)
                + ":\\s+(.*)\\n"
                + pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.SOURCE)
                + ":\\s+(.*)");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            pReport.setAttacker(matcher.group(1));
            pReport.setSource(matcher.group(2));
            Pattern p2 = Pattern.compile("^(.*)\\(([0-9]{1,3}\\|[0-9]{1,3})\\)\\s+K([0-9]{1,3})");
            Matcher matcher2 = p2.matcher(pReport.getSource());
            if (matcher2.find()) {
                String coordinates = matcher2.group(2);
                String[] coord = coordinates.split("\\|");
                if (coord != null && coord.length == 2) {
                    pReport.setXs(Short.parseShort(coord[0]));
                    pReport.setYs(Short.parseShort(coord[1]));
                    return matcher.end();
                }
            }
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine source coordinates in source: {0}", pReport.getSource());
            throw new ParserException("Could not determine source coordinates");
        } else {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine attacker and source in report: {0}", pData);
            throw new ParserException("Could not determine attacker and source");
        }
    }

    private int parseDefender(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.DEFENDER)
                + ":\\s+(.*)\\n"
                + pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.TARGET)
                + ":\\s+(.*)");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            pReport.setDefender(matcher.group(1));
            pReport.setTarget(matcher.group(2));
            Pattern p2 = Pattern.compile("^(.*)\\(([0-9]{1,3}\\|[0-9]{1,3})\\)\\s+K([0-9]{1,3})");
            Matcher matcher2 = p2.matcher(pReport.getSource());
            if (matcher2.find()) {
                String coordinates = matcher2.group(2);
                String[] coord = coordinates.split("\\|");
                if (coord != null && coord.length == 2) {
                    pReport.setXt(Short.parseShort(coord[0]));
                    pReport.setYt(Short.parseShort(coord[1]));
                    return matcher.end();
                }
            }
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine target coordinates in target: {0}", pReport.getSource());
            throw new ParserException("Could not determine source coordinates");
        } else {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to determine defender and target in report: {0}", pData);
            throw new ParserException("Could not determine defender and target");
        }
    }

    private int parseWallDamage(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.WALL_DAMAGE));
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            pReport.addWallDestruction(Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3)), persistence);
            //  System.out.println(matcher.group(2) + " - " + matcher.group(3));
            return matcher.end();
        }
        return pStart;
    }

    private int parseCatapultDamage(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.CATA_DAMAGE));
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            final String buildingName = matcher.group(1);
            DSBuilding building = (DSBuilding) CollectionUtils.find(buildings, new Predicate() {
                public boolean evaluate(Object o) {
                    DSBuilding b = (DSBuilding) o;
                    return b.getName().equals(buildingName);
                }
            });
            if (building != null) {
                pReport.addCataDestruction(building, Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3)));
                return matcher.end();
            } else {
                Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add cata destruction due to an unknown building: {0}", buildingName);
            }
            return matcher.end();
        } else {
            //no damage at all...RED_YELLOW not possible
            if (pReport.getWallDamage() == null) {
                pReport.isNot(ZebraReport.REPORT_COLOR.RED_YELLOW);
            }
        }
        return pStart;
    }

    private int parseSpiedResources(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.SPIED_RESOURCES) + ":\\s+([0-9\\.]+)\\s+([0-9\\.]+)\\s+([0-9\\.]+)");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart) && matcher.groupCount() == 3) {
            pReport.setSpiedWood(Integer.parseInt(matcher.group(1).replaceFirst("\\.", "")));
            pReport.setSpiedClay(Integer.parseInt(matcher.group(2).replaceFirst("\\.", "")));
            pReport.setSpiedIron(Integer.parseInt(matcher.group(3).replaceFirst("\\.", "")));
            return matcher.end();
        }
        return pStart;
    }

    private int parseHaul(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.HAUL) + ":\\s+([\\.0-9]+)\\s([\\.0-9]+)\\s([\\.0-9]+)\\s+([\\.0-9]+)\\/([\\.0-9]+)");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart) && matcher.groupCount() == 5) {
            pReport.setWoodHaul(Integer.parseInt(matcher.group(1).replaceFirst("\\.", "")));
            pReport.setClayHaul(Integer.parseInt(matcher.group(2).replaceFirst("\\.", "")));
            pReport.setIronHaul(Integer.parseInt(matcher.group(3).replaceFirst("\\.", "")));
            pReport.setMaxHaul(Integer.parseInt(matcher.group(5).replaceFirst("\\.", "")));
            return matcher.end();
        }
        return pStart;
    }

    private int parseAcceptance(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.ACCEPTANCE));
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            pReport.setAcceptBefore(Short.parseShort(matcher.group(1)));
            pReport.setAcceptAfter(Short.parseShort(matcher.group(2)));
            pReport.isNot(ZebraReport.REPORT_COLOR.RED_BLUE, ZebraReport.REPORT_COLOR.RED_YELLOW, ZebraReport.REPORT_COLOR.BLUE);
            return matcher.end();
        }
        return pStart;
    }

    private int parseBuildings(String pData, ZebraReport pReport, int pStart) {
        int result = pStart;
        for (DSBuilding building : buildings) {
            if (building.getPlainName().equals("church") || building.getPlainName().equals("church_f")) {
                result = parseChurch(pData, pReport, result);
            } else {
                result = parseBuilding(building.getPlainName(), pData, pConfig.getBuildingMappings().get(building.getPlainName())
                        + "\\s+\\(" + pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.LEVEL)
                        + " ([0-9]+)\\)", pReport, result);
            }
        }
        return result;
    }

    private int parseBuilding(String pName, String pData, String pPattern, ZebraReport pReport, int pStart) {
        Pattern p = Pattern.compile(pPattern);
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            final String buildingName = pName;
            DSBuilding building = (DSBuilding) CollectionUtils.find(buildings, new Predicate() {
                public boolean evaluate(Object o) {
                    DSBuilding b = (DSBuilding) o;
                    return b.getPlainName().equals(buildingName);
                }
            });
            if (building != null) {
                pReport.addSpyedBuilding(building, Integer.parseInt(matcher.group(1)));
            } else {
                Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add spied building due to an unknown building name: {0}", buildingName);
            }
            //System.out.println(pName + " " + matcher.group(1));
            return matcher.end();
        }
        return pStart;
    }

    private int parseChurch(String pData, ZebraReport pReport, int pStart) {
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.FIRST_CURCH)
                + "\\s+\\("
                + pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.LEVEL)
                + " ([0-9]+)\\)");
        Matcher matcher = p.matcher(pData);
        if (matcher.find(pStart)) {
            //first church
            DSBuilding building = (DSBuilding) CollectionUtils.find(buildings, new Predicate() {
                public boolean evaluate(Object o) {
                    DSBuilding b = (DSBuilding) o;
                    return b.getPlainName().equals("church_f");
                }
            });
            if (building != null) {
                pReport.addSpyedBuilding(building, Integer.parseInt(matcher.group(1)));
            } else {
                Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add first church as it was not found in the database");
            }
            // System.out.println("church_f " + " " + matcher.group(1));
            return matcher.end();
        } else {
            p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.CHURCH)
                    + "\\s+\\("
                    + pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.LEVEL)
                    + " ([0-9]+)\\)");
            matcher = p.matcher(pData);
            if (matcher.find()) {
                DSBuilding building = (DSBuilding) CollectionUtils.find(buildings, new Predicate() {
                    public boolean evaluate(Object o) {
                        DSBuilding b = (DSBuilding) o;
                        return b.getPlainName().equals("church");
                    }
                });
                if (building != null) {
                    pReport.addSpyedBuilding(building, Integer.parseInt(matcher.group(1)));
                } else {
                    Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add church as it was not found in the database");
                }
                //System.out.println("church" + " " + matcher.group(1));
                return matcher.end();
            }
        }
        return pStart;
    }

    private void parseTroops(String pData, ZebraReport pReport, int pStart) throws ParserException {
        System.out.println(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.AMOUNT) + ":\\s+" + getTroopsPattern());
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.AMOUNT) + ":\\s+" + getTroopsPattern());
        Matcher matcher = p.matcher(pData);

        int cnt = 0;
        int pos = pStart;
        Hashtable<DSUnit, Integer> attackers = new Hashtable<DSUnit, Integer>();
        String[] supportedUnits = pConfig.getUnits();
        while (matcher.find(pos)) {
            if (cnt == 2) {
                Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to parse troop amounts in line: {0}", matcher.group(1));
                throw new ParserException("Could not parse troop amounts");
            }

            String[] split = matcher.group(1).split("\\s");
            for (int i = 0; i < supportedUnits.length; i++) {
                final String plainName = supportedUnits[i];
                DSUnit unit = (DSUnit) CollectionUtils.find(units, new Predicate() {
                    public boolean evaluate(Object o) {
                        DSUnit b = (DSUnit) o;
                        return b.getPlainName().equals(plainName);
                    }
                });
                if (cnt == 0) {
                    if (unit != null) {
                        Integer amount = Integer.parseInt(split[i]);
                        pReport.addAttackerUnit(unit, amount);
                        attackers.put(unit, amount);
                    } else {
                        Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add unit {0} as it was not found in the database", plainName);
                    }
                } else {
                    if (unit != null) {
                        pReport.addDefenderUnit(unit, Integer.parseInt(split[i]));
                    } else {
                        Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add unit {0} as it was not found in the database", plainName);
                    }
                }
                // System.out.println("Add Amounts " + cnt + ": " + units[i] + " " + split[i]);
            }
            cnt++;
            pos = matcher.end();
        }

        if (cnt == 0) {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed determine troop amount in report: {0}", pData.substring(pStart));
            throw new ParserException("Could not determine troop amount");
        }

        p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.LOSSES) + ":\\s+" + getTroopsPattern());
        matcher = p.matcher(pData);
        cnt = 0;
        pos = pStart;
        boolean anyAttackerDied = false;
        while (matcher.find(pos)) {
            if (cnt == 2) {
                Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to parse losses in line: {0}", matcher.group(1));
                throw new ParserException("Could not parse losses");
            }
            String[] split = matcher.group(1).split("\\s");

            for (int i = 0; i < supportedUnits.length; i++) {
                final String plainName = supportedUnits[i];
                DSUnit unit = (DSUnit) CollectionUtils.find(units, new Predicate() {
                    public boolean evaluate(Object o) {
                        DSUnit b = (DSUnit) o;
                        return b.getPlainName().equals(plainName);
                    }
                });

                if (unit != null) {
                    Integer diedAmount = Integer.parseInt(split[i]);
                    if (cnt == 0) {
                        pReport.addLostAttackerUnit(unit, diedAmount);
                        int amountBefore = attackers.get(unit);
                        if (diedAmount > 0) {
                            //someone has died
                            pReport.isNot(ZebraReport.REPORT_COLOR.GREEN);
                            anyAttackerDied = true;
                        }
                        if (amountBefore > diedAmount) {
                            //someone survived
                            pReport.isNot(ZebraReport.REPORT_COLOR.RED, ZebraReport.REPORT_COLOR.RED_BLUE, ZebraReport.REPORT_COLOR.RED_YELLOW);
                        }
                    } else {
                        pReport.addLostDefenderUnit(unit, diedAmount);
                    }
                } else {
                    Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add unit {0} as it was not found in the database", plainName);
                }
                //  System.out.println("Add Losses " + cnt + ": " + units[i] + " " + split[i]);
            }

            cnt++;
            pos = matcher.end();
        }

        if (cnt == 0) {
            Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed determine died troops  amount in report: {0}", pData);
            throw new ParserException("Could not determine died troops amount");
        }
        if (!anyAttackerDied) {
            pReport.isNot(ZebraReport.REPORT_COLOR.YELLOW);
        }
    }

    private int parseTroopsOnTheWay(String pData, ZebraReport pReport, int pStart) throws ParserException {

        long s = System.currentTimeMillis();
        Pattern p = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.ON_THE_WAY) + getTroopsPattern());
        Matcher matcher = p.matcher(pData);
        String[] supportedUnits = pConfig.getUnits();
        if (matcher.find(pStart)) {
            String[] split = matcher.group(1).split("\\s");
            for (int i = 0; i < supportedUnits.length; i++) {
                final String plainName = supportedUnits[i];
                DSUnit unit = unitsByPlainName.get(plainName);
                if (unit != null) {
                    pReport.addTroopsOnTheWay(unit, Integer.parseInt(split[i]));
                } else {
                    Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add unit {0} as it was not found in the database", plainName);
                }
                // System.out.println("Add OTW: " + units[i] + " " + split[i]);
            }
            return matcher.end();
        }
        return pStart;
    }

    private void parseTroopsOutside(String pData, ZebraReport pReport, int pStart) throws ParserException {
        Pattern defenderBlockPattern = Pattern.compile(pConfig.getValueByPatternId(ParserPattern.PARSER_PATTERN_ID.OUTSIDE));
        Matcher defenderBlockMatcher = defenderBlockPattern.matcher(pData);
        if (defenderBlockMatcher.find(pStart)) {
            Pattern troopLinePattern = Pattern.compile(".+\\s+K\\d+\\s+(" + getTroopsPattern() + ")");
            Matcher troopLineMatcher = troopLinePattern.matcher(pData);
            int currentPos = defenderBlockMatcher.end();
            String[] supportedUnits = pConfig.getUnits();
            while (troopLineMatcher.find(currentPos)) {
                String troops = troopLineMatcher.group(1);
                String[] split = troops.split("\\s");
                for (int i = 0; i < supportedUnits.length; i++) {
                    final String plainName = supportedUnits[i];
                    DSUnit unit = (DSUnit) CollectionUtils.find(units, new Predicate() {
                        public boolean evaluate(Object o) {
                            DSUnit b = (DSUnit) o;
                            return b.getPlainName().equals(plainName);
                        }
                    });
                    if (unit != null) {
                        pReport.addTroopsOutside(unit, Integer.parseInt(split[i]));
                    } else {
                        Logger.getLogger(ZebraReportParser.class.getName()).log(Level.WARNING, "Failed to add unit {0} as it was not found in the database", plainName);
                    }
                    //System.out.println("Add OUTS: " + units[i] + " " + split[i]);
                }
                currentPos += troopLineMatcher.group(0).length();
            }
        }
    }

    public String getTroopsPattern() {
        StringBuilder b = new StringBuilder();
        b.append("([0-9x]+");
        String[] supportedUnits = pConfig.getUnits();
        for (int i = 1; i < supportedUnits.length; i++) {
            b.append("\\s+[0-9x]+");
        }
        b.append(")");
        return b.toString();
    }

    public static void main(String[] args) throws Exception {
        ZebraReport report = new ZebraReport();
        // ZebraReportParser.parseMoral("  Moral: 100%", report);
        PersistenceImpl persistence = new PersistenceImpl();
        new ZebraReportParser(persistence, "de43").parseLuck("Glück (aus Sicht des Angreifers)-14.1%		", report, 0);

        new ZebraReportParser(persistence, "zz2").parseTroops("Quantity: 	0	0	0	0	300	0	0	0	0	0	0\n"
                + "Losses: 	0	0	0	0	0	0	0	0	0	0	0", report, 0);


        // ZebraReportParser.parseLuck("Glück (aus Sicht des Angreifers)\n"
        //         + "Pech 	\n"
        //         + "	Glück 	24.5%", report, 0);
        System.out.println(report.getLuck());

        String b = new String("SpionageErspähte Rohstoffe:	0 0 0 \n"
                + "Gebäude:	Hauptgebäude (Stufe 20)\n"
                + "Kaserne (Stufe 18)\n"
                + "Stall (Stufe 9)\n"
                + "Werkstatt (Stufe 1)\n"
                + "Schmiede (Stufe 16)\n"
                + "Versammlungsplatz (Stufe 1)\n"
                + "Marktplatz (Stufe 10)\n"
                + "Holzfäller (Stufe 29)\n"
                + "Lehmgrube (Stufe 29)\n"
                + "Eisenmine (Stufe 27)\n"
                + "Bauernhof (Stufe 20)\n"
                + "Speicher (Stufe 30)\n"
                + "Versteck (Stufe 8)\n");
        new ZebraReportParser(persistence, "de43").parseBuildings(b, report, 0);


        // parseAttacker("Angreifer: ---\nDorf: MeinDorf (123|123) (123|213) K21", report);
        //parseSendTime("Gesendet	17.01.10 21:27", report);
        //System.out.println(report.getLuck());
        //parseDefender("Verteidiger: ---\nDorf: MeinDorf (123|123) (123|213) K21", report);
        // parseDefender("Verteidiger: ---\nDorf: MeinDorf (123|123) (123|213) K21", report);
        // parseSpiedResources("Erspähte Rohstoffe: 12 22 42.123", report);
        //parseHaul("Beute: 12 22 21 123/234.234", report);
        new ZebraReportParser(persistence, "de43").parseAcceptance("Zustimmung: Gesunken von 16 auf -18", report, 0);
        System.out.println(report.getAcceptBefore() + " - " + report.getAcceptAfter());
        /* parseBuildings("Gebäude:	Hauptgebäude (Stufe 20)"
         + "Kaserne (Stufe 18)"
         + "Stall (Stufe 9)"
         + "Werkstatt (Stufe 1)"
         + "Schmiede (Stufe 16)"
         + "Kirche (Stufe 1)"
         + "Adelshof (Stufe 1)"
         + "Versammlungsplatz (Stufe 1)"
         + "Statue (Stufe 1)"
         + "Marktplatz (Stufe 10)"
         + "Holzfäller (Stufe 29)"
         + "Lehmgrube (Stufe 29)"
         + "Eisenmine (Stufe 27)"
         + "Bauernhof (Stufe 20)"
         + "Speicher (Stufe 30)"
         + "Versteck (Stufe 8)"
         + "Wall (Stufe 1)", report);*/
        new ZebraReportParser(persistence, "de43").parseWallDamage("Schaden durch Rammböcke: Wall beschädigt von Level 12 auf Level 3", report, 0);
        // parseCatapultDamage("Schaden durch Katapultbeschuss: Hauptgebäude beschädigt von Level 20 auf Level 10", report);
        // System.out.println(report.getAcceptBefore() + " - " + report.getAcceptAfter());
        /*  parseTroops("Angreifer:	poster007"
         + "Dorf:	Barbarendorf (643|775) K76"
         + "Anzahl:	0	0	4443	0	32	2200	0	0	200	0	0	0"
         + "Verluste:	0	0	1	0	0	1	0	0	0	0	0	0"
         + ""
         + "Verteidiger:	Kennek"
         + "Dorf:	64:3|7:77 (643|777) K76"
         + "Anzahl:	73	0	0	0	0	0	0	0	0	0	0	0"
         + "Verluste:	10	0	0	0	0	0	0	0	0	0	0	0", report);*/

        /*  parseTroopsOnTheWay("Truppen des Verteidigers, die unterwegs waren\n"
         + "12	13	0	0	0	0	0	0	0	0	0	0", report);*/
        /* parseTroopsOutside("Truppen des Verteidigers in anderen Dörfern\n"
         + "Barbarendorf (602|761) K76	0	0	2642	0	100	1423	0	0	300	0	0	0 \n"
         + "Barbarendorf (123|41) K12	1222	1222	0	0	0	0	0	0	0	0	0	0", report);
         */
    }
}
